﻿using System;
using System.Collections.Generic;
using System.Linq;
using StockAccessors;
using StockModel;
using Utils;

namespace StockEngine.RealTime
{
	internal class Acquisitor : PropertyChangedNotifier
	{
		#region Attributes

		private readonly IStockDataAccessor _accessor;
		private readonly List<EngineStockGroup> _groups;
		private readonly QuotationDataSource _quotationDataSource;

	    private int _skippedItems;

		#endregion

        #region Properties

	    public int SkippedItems
	    {
	        get
	        {
                return _skippedItems;
	        }
            set
            {
                if (_skippedItems != value)
                {
                    _skippedItems = value;
                    base.OnPropertyChanged("SkippedItems");
                }
            }
	    }

        #endregion

        #region Public Methods

        public Acquisitor(IStockDataAccessor accessor, List<EngineStockGroup> groups, QuotationDataSource quotationDataSource)
		{
			_accessor = accessor;
			_groups = groups;
			_quotationDataSource = quotationDataSource;
		}

		public void ProcessGroups()
		{
			DateTime generatedTime = DateTime.Now;

			List<EngineStockGroup> initializedGroups = (from g in _groups
												  where g.IsInitialized
												  select g).ToList();

			foreach (EngineStockGroup group in initializedGroups)
			{
				AccessorGroup accessorGroup = _accessor.Groups.FirstOrDefault(g => g != null && g.Name == @group.Name);

				if (accessorGroup != null) // Can be null if the group is not offered by the Accessor
				{
					ObservableCollection2<StockGroupItem> items = group.Items.GetReadLock();
					try
					{
						foreach (StockGroupItem groupItem in items)
						{
							if (!group.RemovedItems.Contains(groupItem))
							{
								DateTime now = DateTime.Now;
								AccessorStockQuote webStockQuote = accessorGroup.Stocks.FirstOrDefault(s => s.Symbol == groupItem.Item.Id.Code);
								if (webStockQuote != null)
									// Can be null if a future has arrived to its expiry date or if the data is from another day
								{
									try
									{
										if (webStockQuote.Acquisition.Day == now.Day)
										{
											this.ProcessStock(groupItem.Item, generatedTime, webStockQuote, group.Market);
										}
									}
									catch (Exception ex)
									{
										ExceptionLogger.LogException("Exception received while processing stock " + groupItem.Item.Id.Name + ". Skipping this quotation.", ex, false);
										this.SkippedItems++;
									}
								}
								else
								{
									group.RemovedItems.Add(groupItem);
								}
							}
						}
					}
					finally
					{
						group.Items.FreeReadLock();
					}
				}
			}
		}

		#endregion

		#region Private Methods

		private void ProcessStock(Stock stock, DateTime generatedTime, AccessorStockQuote accessorStockQuote, ExchangeMarket market)
		{
			QuotationDailyInfo quotationDailyInfo = stock.QuotationCalendar.GetDay(DateTime.Now.Date);

			Quotation newQuotation = AccessorStockToQuotationConverter.ConvertItem(accessorStockQuote);
			
			newQuotation.Date = generatedTime;
			newQuotation.Type = market.GetQuoteType(newQuotation.Date);

			ObservableCollection2<Quotation> quotations = quotationDailyInfo.Quotations.GetWriteLock();
			Quotation lastQuotation = quotations.Count > 0 ? quotations[quotations.Count - 1] : null;

			try
			{
				if (lastQuotation != null)
				{
					quotations.AddRange(QuotationCollectionFiller.CreateOneSecondSpacedCollection(lastQuotation, newQuotation, market));
				}
				else
				{
					quotations.Add(newQuotation);
				}
			}
			finally 
			{
				quotationDailyInfo.Quotations.FreeWriteLock();
			}
			

			if (lastQuotation == null || !lastQuotation.Equals(newQuotation))
			{
				if (lastQuotation != null && lastQuotation.Type != newQuotation.Type)
				{
					newQuotation.Acquisition = newQuotation.Date; // When saving a quotation to store the market status change we use the generated time instead of the acquisition time
				}												  // because the time to store is the time when the market changed its status, not the time when the quotation was captured.

				_quotationDataSource.Save(stock, newQuotation);
			}
		}

		#endregion
	}
}
